﻿
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>

#define LP_CORE

#include "lp_conf.h"
#include "lp_storage.h"


typedef struct Block Block;
struct Block{
    int used;
    LPHandle h;
    Block * next;
    Block * last;
};

typedef struct Slot Slot;
struct Slot {
    char * buf;
    uint32_t count;
    Slot * next;
};

struct LPStorage{
    uint32_t hIdx;
    uint32_t count;
    uint32_t elementSize;
    Slot * slot;
    Slot * slotEnd;

    Block * block;
    Block * blockEnd;
    Block * blockCur;
};

#define MAX_EXPEND_SIZE     10240

static Slot *
newSlot(uint32_t elementSize, uint32_t count) {
    if(count > MAX_EXPEND_SIZE){
        count = MAX_EXPEND_SIZE;
    }
    Slot * s = malloc(sizeof(Slot));
    s->buf = malloc((sizeof(Block) + elementSize) * count);
    memset(s->buf, 0, (sizeof(Block) + elementSize) * count);
    s->count = count;
    s->next = NULL;
    return s;
}

static void
freeSlot(Slot * s) {
    Slot * tmp;
    while (s) {
        tmp = s;
        s = s->next;
        free(tmp->buf);
        free(tmp);
    }
}

static char *
regInSlot(LPStorage * storage, LPHandle h) {
    Block * block;
    uint32_t hash;
    Slot * s = storage->slot;
    while (s) {
        hash = h % s->count;
        block = (Block *)&(s->buf[hash * (sizeof(Block) + storage->elementSize)]);
        if (!block->used) {
            block->used = 1;
            block->h = h;
            if(!storage->block){
                storage->block = block;
                storage->blockEnd = block;
            }else{
                storage->blockEnd->next = block;
                block->last = storage->blockEnd;
                storage->blockEnd = block;
            }
            return ((char *)block) + sizeof(Block);
        }
        s = s->next;
    }
    return NULL;
}

static Block *
findInSlot(LPStorage * storage, LPHandle h) {
    Block * block;
    uint32_t hash;
    Slot * s = storage->slot;
    while (s) {
        hash = h % s->count;
        block = (Block *)&(s->buf[hash * (sizeof(Block) + storage->elementSize)]);
        if(block->used && block->h == h){
            return block;
        }
        s = s->next;
    }
    return NULL;
}


LP_API LPStorage *
lpNewStorage (unsigned elementSize, unsigned initCount){
    if (initCount < 1)
        initCount = 1;
    LPStorage * storage = malloc(sizeof(LPStorage));
    memset(storage, 0, sizeof(LPStorage));
    storage->hIdx = 0;
    storage->count = initCount;
    storage->elementSize = elementSize;
    storage->slot = newSlot(elementSize, initCount);
    storage->slotEnd = storage->slot;
    return storage;
}

LP_API void
lpFreeStorage (LPStorage * storage){
    freeSlot(storage->slot);
    free(storage);
}

LP_API LPHandle
lpRegInStorage (LPStorage * storage, void ** p){
    LPHandle h;
    char * buf;
    uint32_t hIdx = storage->hIdx;
    for(;;){
        for(uint32_t i = 0; i < storage->count; i ++){
            h = ++hIdx;
            if (h == 0){ h = ++hIdx; }
            buf = regInSlot(storage, h);
            if(buf){
                *p = buf;
                storage->hIdx = hIdx;
                return h;
            }
        }
        lpExpandStorage(storage, storage->slotEnd->count * 2);
    }
}

LP_API void *
lpFindInStorage (LPStorage * storage, LPHandle h){
    Block * block = findInSlot(storage, h);
    if (block) {
        return ((char *)block) + sizeof(Block);
    }
    return NULL;
}

LP_API int
lpRecInStorage (LPStorage * storage, LPHandle h){
    Block * block = findInSlot(storage, h);
    if (block) {
        if(storage->block == block){
            storage->block = block->next;
        }
        if(storage->blockEnd == block){
            storage->blockEnd = block->last;
        }
        if(storage->blockCur == block){
            storage->blockCur = block->next;
        }
        if(block->last){
            block->last->next = block->next;
        }
        if(block->next){
            block->next->last = block->last;
        }
        memset(block, 0, sizeof(Block)+ storage->elementSize);
        return 0;
    }
    return 1;
}

LP_API void
lpExpandStorage (LPStorage * storage, uint32_t count){
    Slot * s = newSlot(storage->elementSize, count);
    storage->slotEnd->next = s;
    storage->slotEnd = s;
    storage->count += count;
}

LP_API void
lpBeginStorage(LPStorage * storage){
    storage->blockCur = storage->block;
}

LP_API int
lpNextStorage(LPStorage * storage, LPHandle * h, void ** p){
    Block * cur = storage->blockCur;
    if(!cur){
        return 0;
    }
    *h = cur->h;
    *p = ((char *)cur) + sizeof(Block);
    storage->blockCur = cur->next;
    return 1;
}

